#ifdef _WIN_
#include "../inc/skyTrace.h"
#include "../inc/skyPolicy.h"
namespace Sky
{
	namespace NThreads
	{
typedef struct _xWorker
{
	xTask* task;
	i32    workerId;
	_xWorker* prev;
	_xWorker* next;
}xWorker;

struct _xWorkPool 
{
	HANDLE hThread;
	TUint32 threadId;

	i32 workers;
	HANDLE* totalEvent;
	xWorker* workers_queue;
	xWorker* busy_workers_queue;
	xWorker* idle_workers_queue;
	i32 exit_thread_numb;
	i32 add_worker_event_numb;
	i32 file_event_numb;
	i32 work_done_numb;	
	i32 total_waited_events;
	i32 total_events;

	i32 exit_thread_base_index;
	i32 add_new_worker_events_base_index;
	i32 file_reading_events_base_index;
	i32 work_done_events_base_index;

	HANDLE* exit_thread_event_base;
	HANDLE* add_new_worker_events_base;
	HANDLE* file_reading_events_base;
	HANDLE* work_done_events_base;

	HANDLE  get_exit_thread_event(i32 id){return totalEvent[exit_thread_base_index+id];}
	HANDLE  get_add_new_worker_event(i32 id){return totalEvent[add_new_worker_events_base_index+id];}
	HANDLE	get_file_reading_event(i32 id){return totalEvent[file_reading_events_base_index+id];}
	HANDLE	get_work_done_event(i32 id){return totalEvent[work_done_events_base_index+id];}
};

Bool attach_to_idle_workers_queue(xWorkPool* pool, xWorker* worker )
{
	if(pool->idle_workers_queue == vNull){
		pool->idle_workers_queue = worker;
		worker->prev = worker;
		worker->next = worker;
		return vTrue;
	}else{
		worker->prev = pool->idle_workers_queue->prev;
		worker->next = pool->idle_workers_queue;
		pool->idle_workers_queue->prev->next = worker;
		pool->idle_workers_queue->prev = worker;
		return vTrue;
	}
}

Bool attach_to_busy_workers_queue(xWorkPool* pool, xWorker* worker )
{
	if(pool->busy_workers_queue == vNull){
		pool->busy_workers_queue = worker;
		worker->prev = worker;
		worker->next = worker;
		return vTrue;
	}else{
		worker->prev = pool->busy_workers_queue->prev;
		worker->next = pool->busy_workers_queue;
		pool->busy_workers_queue->prev->next = worker;
		pool->busy_workers_queue->prev = worker;
		return vTrue;
	}
}

Bool detach_from_idle_workers_queue(xWorkPool* pool, xWorker* worker )
{
	if(pool->idle_workers_queue == vNull)
		return vFalse;
	if(pool->idle_workers_queue == worker) {
		pool->idle_workers_queue = pool->idle_workers_queue->next;
		if(pool->idle_workers_queue == worker){
			pool->idle_workers_queue = vNull;
			return vTrue;
		}else {
			worker->prev->next = worker->next;
			worker->next->prev = worker->prev;
		}
	}else {
		worker->prev->next = worker->next;
		worker->next->prev = worker->prev;
	}
	return vTrue;
}

Bool detach_from_busy_workers_queue(xWorkPool* pool, xWorker* worker )
{
	if(pool->busy_workers_queue == vNull)
		return vFalse;
	if(pool->busy_workers_queue == worker) {
		pool->busy_workers_queue = pool->busy_workers_queue->next;
		if(pool->busy_workers_queue == worker){
			pool->busy_workers_queue = vNull;
			return vTrue;
		}else {
			worker->prev->next = worker->next;
			worker->next->prev = worker->prev;
		}
	}else {
		worker->prev->next = worker->next;
		worker->next->prev = worker->prev;
	}
	return vTrue;
}

xWorker* GetAnIdleWorker(xWorkPool* pool)
{
	if(pool->idle_workers_queue)
		return pool->idle_workers_queue;
	else
		return vNull;
}

Bool SendToWork(xWorkPool* pool,xWorker* worker )
{
	if(detach_from_idle_workers_queue(pool,worker)){
		::SetEvent(pool->get_add_new_worker_event(worker->workerId));
		return vTrue;
	}
	else {
		myTrace0A(S,"Not enough workers!!");
		return vFalse;
	}
}

Void xThread_OnAddWorker(xWorkPool* pool,i32 id)
{
	xTask* task = pool->workers_queue[id].task;
	Bool suc;

	attach_to_busy_workers_queue(pool,&pool->workers_queue[id]);
	::ResetEvent(pool->get_add_new_worker_event(id));

	suc = task->onDoTask(task,pool->get_file_reading_event(id));

	if(!suc){
		::SetEvent(pool->get_file_reading_event(id));
	}
}

Void xThread_OnDelWorker(xWorkPool* pool,i32 id){
	xTask* task = pool->workers_queue[id].task;
	detach_from_busy_workers_queue(pool,&pool->workers_queue[id]);
	::ResetEvent(pool->get_file_reading_event(id));

	task->onDoTaskResult(task);
	::SetEvent(pool->get_work_done_event(id));
}

Void OnWorkDone(xWorkPool* pool,i32 id){

	xTask* task = pool->workers_queue[id].task;

	task->onTaskDone(task);

	attach_to_idle_workers_queue(pool,&pool->workers_queue[id]);
	::ResetEvent(pool->get_work_done_event(id));
	
}

Bool xWorkPool_CheckJobDone(xWorkPool* pool)
{
	u32 ret;
	ret = WaitForMultipleObjects(pool->work_done_numb,pool->work_done_events_base,vFalse,0);
	if(ret >= WAIT_OBJECT_0 && ret < pool->work_done_numb){
		i32 id = ret - WAIT_OBJECT_0;
		OnWorkDone(pool,id);	
		return vTrue;
	}
	return vFalse;
}

Bool xWorkPool_Assign(xWorkPool* pool, xTask* task)
{
	xWorker* worker = GetAnIdleWorker(pool);
	if(worker == vNull){
		return vFalse;
	}else{
		worker->task = task;
		return SendToWork(pool,worker);
	}
	return vFalse;
}

i32 xThread_Processing( Void* p )
{
	u32 ret;
	xWorkPool* pool = (xWorkPool*)p;
	for(;;){
		ret = WaitForMultipleObjects(pool->total_waited_events,pool->totalEvent,vFalse,1000);
		if(ret >= WAIT_OBJECT_0 && ret < WAIT_OBJECT_0 + pool->exit_thread_numb) {
			return 0;
		}
		else if (ret>=WAIT_OBJECT_0 + pool->add_new_worker_events_base_index && ret < WAIT_OBJECT_0 + pool->add_new_worker_events_base_index + pool->add_worker_event_numb)
		{
			i32 id = ret - WAIT_OBJECT_0 - pool->add_new_worker_events_base_index;
			xThread_OnAddWorker(pool,id);
		}else if(ret>=WAIT_OBJECT_0 + pool->file_reading_events_base_index && ret < WAIT_OBJECT_0 + pool->file_reading_events_base_index + pool->file_event_numb)
		{
			i32 id = ret - WAIT_OBJECT_0 - pool->file_reading_events_base_index;
			xThread_OnDelWorker(pool,id);
		}else if( ret >= WAIT_ABANDONED_0 + pool->add_new_worker_events_base_index && ret < WAIT_ABANDONED_0 + pool->add_new_worker_events_base_index + pool->add_worker_event_numb)
		{
			i32 id = ret - WAIT_OBJECT_0 - pool->add_new_worker_events_base_index;
			xTask* task = pool->workers_queue[id].task;
		}else if(ret == WAIT_TIMEOUT){

		}else
		{
			return 0;
		}
	}
	return 0;
}

xWorkPool* xWorkPool_Create(i32 workers_number)
{
	xWorkPool* pool = xFactory<xWorkPool>::Create();
	pool->workers_queue = xFactory<xWorker>::CreateArray(workers_number);// xWorker[workers_number];
	pool->exit_thread_numb = 1;
	pool->add_worker_event_numb = workers_number;
	pool->file_event_numb = workers_number;
	pool->work_done_numb = workers_number;
	pool->total_waited_events = 1 + workers_number + workers_number;
	pool->total_events = pool->total_waited_events + workers_number;
	pool->totalEvent = xFactory<HANDLE>::CreateArray(pool->total_events);// HANDLE[pool->total_events];

	pool->exit_thread_base_index = 0;
	pool->add_new_worker_events_base_index = (pool->exit_thread_base_index + pool->exit_thread_numb);
	pool->file_reading_events_base_index = (pool->add_new_worker_events_base_index + pool->add_worker_event_numb);
	pool->work_done_events_base_index = (pool->file_reading_events_base_index + pool->file_event_numb);

	pool->exit_thread_event_base =		 (&pool->totalEvent[pool->exit_thread_base_index]);
	pool->add_new_worker_events_base =	 (&pool->totalEvent[pool->add_new_worker_events_base_index]);
	pool->file_reading_events_base =	 (&pool->totalEvent[pool->file_reading_events_base_index]);
	pool->work_done_events_base =		 (&pool->totalEvent[pool->work_done_events_base_index]);


	for(i32 i = 0;i<pool->total_events;i++){
		pool->totalEvent[i] = CreateEvent(vNull,vTrue,vFalse,vNull);
	}
	for(i32 i = 0;i< workers_number;i++)
	{
		pool->workers_queue[i].task = vNull;
		pool->workers_queue[i].workerId = i;
	}

	for(i32 i = 1;i < workers_number - 1;i++){
		pool->workers_queue[i].next = &pool->workers_queue[i+1];
		pool->workers_queue[i].prev = &pool->workers_queue[i-1];
	}

	pool->workers_queue[0].next = &pool->workers_queue[1];
	pool->workers_queue[0].prev = &pool->workers_queue[workers_number-1];
	pool->workers_queue[workers_number-1].prev = &pool->workers_queue[workers_number-2];
	pool->workers_queue[workers_number-1].next = &pool->workers_queue[0];

	pool->idle_workers_queue = pool->workers_queue;
	pool->busy_workers_queue = vNull;

	pool->hThread=CreateThread((LPSECURITY_ATTRIBUTES)vNull,0,(LPTHREAD_START_ROUTINE)xThread_Processing,(Void*)pool,0,&pool->threadId);
	if(pool->hThread == INVALID_HANDLE_VALUE){
		xWorkPool_Delete(pool);
		pool = vNull;
		return vNull;
	}
	return pool;
}
Void xWorkPool_Delete(xWorkPool* pool)
{
	::SetEvent(pool->get_exit_thread_event(0));
	WaitForSingleObject(pool->get_exit_thread_event(0),INFINITE);
	CloseHandle(pool->hThread);
	pool->hThread = vNull;
	for(i32 i = 0;i<pool->total_events;i++){
		if(pool->totalEvent[i]){
			CloseHandle(pool->totalEvent[i]);
			pool->totalEvent[i]= vNull;
		}
	}
	if(pool){
		xFactory<HANDLE>::DeleteArray(pool->totalEvent,pool->total_events);
		xFactory<xWorker>::DeleteArray(pool->workers_queue,pool->add_worker_event_numb);
		xFactory<xWorkPool>::Delete(pool);
	}
}
}
}
#endif
